import copy,os,shutil,yaml,sys,json
import tornado.web                          #第三方模块tornado       需要下载  web小型框架 使用IO多路复用
import tornado.ioloop                       #第三方模块tornado       需要下载  web小型框架 使用IO多路复用
import os,sys,json                          #内置标准库              os文件操作、sys系统操作、json数据类型
import yaml                                 #第三方模块yaml          需要下载  是一个配置文件解析的第三方模块
pro_dir=os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.path.append(pro_dir)
import xml.etree.ElementTree as ET#python自带的处理xml的模块
class base_class(tornado.web.RequestHandler):pass
g=None
html_head=r'''
<html lang="en" translate="no">
<head>
<meta charset="utf-8">
<meta name="google" content="notranslate"/>

<!-- head -->

<script type="text/javascript"  src="\static_base\vue2.5.17.js"></script>
<script type="text/javascript"  src="\static_base\element_ui_2.0.3_index.js"></script>
<link rel="stylesheet" href="\static_base\iconfont.css">
<link rel="stylesheet" href="\static_base\element_ui.css">
<link rel="stylesheet" href="\static_base\my.css">
<script type="text/javascript"  src="\static_base\base.js"></script>
<script type="text/javascript"  src="\static\base_vue_template2.js"></script>
<link rel="stylesheet" href="\static\base_vue_template2.css">
<!-- head -->
</head>
'''
html_head_data=r'''
<html lang="en" translate="no">
<head>
<meta charset="utf-8">	
<meta name="google" content="notranslate" />
<script type="text/javascript"  src="\static_base\vue2.5.17.js"></script>
<script type="text/javascript"  src="\static_base\element_ui_2.0.3_index.js"></script>
<link rel="stylesheet" href="\static_base\iconfont.css">
<link rel="stylesheet" href="\static_base\element_ui.css">
<script type="text/javascript"  src="\static_base\base.js"></script>
<script type="text/javascript"  src="\static\base_vue_template2.js"></script>
<link rel="stylesheet" href="\static\base_vue_template2.css">
</head>
'''
#将组件中的python文件直接生成
class template_create:
    def __init__(self):
        self.print_flag=True
        self.static_base_path=pro_dir+os.sep+'_static_dir'
        print(self.static_base_path)
    def edit_init(self):
        if self.print_flag is True:print("开始生成base.js")
        self.my_js()
        if self.print_flag is True:print("开始生成base_vue_template.js and css")
        self._create_vue_template_edit()
    def config_init(self,config,obj):
        
        self.config=config
        self.g=obj.get("g")
        if self.print_flag is True:print("获取 配置文件中 使用的模板 保存到集合中")
        self.all_use_template_names_set=self._get_all_use_template_names_set()
        if self.print_flag is True:print("获取所有的模板信息 模板名称:存放模板的完成文件夹路径")
        self.all_template_info=self._get_template_info()
        if self.print_flag is True:print("根据用到的模板 生成 base_vue_template2.js css 文件")
        self._create_vue_template()
        if self.print_flag is True:print("根据用到的模板 生成 生成python的后端代码并执行")
        url_lst1=self._create_python_template()
        if self.print_flag is True:print("根据页面编辑 生成对应的页面 js、css、后端响应url和class")
        url_lst2=self.tornado_make_html_and_url()
        res=url_lst1+url_lst2
        return res
    def my_js(self):
        my_js_dir=pro_dir+os.sep+'_web'+os.sep+"web_js"
        str_js=''
        file_lst=self._get_file_paths(my_js_dir)
        lst_dir=sorted(file_lst,key=lambda item:int(os.path.basename(item).split('_')[0]) )
        for path in lst_dir:
            try:
                with open(path,'r',encoding='utf-8')as f:
                    str_js=str_js+'\n'+f.read()
            except Exception as e:print(e)
        my_js_path=self.static_base_path+os.sep+'base.js'
        with open(my_js_path,'w',encoding='utf-8')as f:
            f.write(str_js)
        if self.print_flag is True:print("base.js生成完成:",my_js_path)
    def _get_file_paths(self,directory):  
        file_paths = []  
        for root, dirs, files in os.walk(directory):  
            for file in files:  
                file_path = os.path.join(root, file)  
                file_paths.append(file_path)  # 将完整路径和文件名保存到列表中  
        return file_paths      
    def _get_folder_paths3(self,directory):  
        folder_paths = []  
        # print(directory,"directory")
        for root, dirs, files in os.walk(directory):  
            if 'main.js'in files:
                try:
                    with open(root+os.sep+'main.yaml', 'r',encoding='utf-8') as f:
                        config = yaml.load(f, Loader=yaml.FullLoader)
                        template_name=config['name']
                        folder_paths.append([template_name,root])  # 将当前目录的完整路径添加到列表中  
                except:pass
        # print(folder_paths,"folder_paths")
        return folder_paths  
    #获取所有使用的模板
    def _get_all_use_template_names_set(self):
        res=set()
        #获取 页面配置  根据所有定义的页面 检查使用了 哪些模块
        page_config=self.config['page_config']
        #遍历所有的页面
        for key in page_config:
            #获取页面 使用vue组件的树形 数据 json
            page_use_compontent=page_config[key]['page_use_compontent']['page']
            #非递归添加使用的模板名称到 res中
            temp_lst=[[]]
            while temp_lst:
                #从待处理列表中取出 要处理的对象 路由
                temp_path_lst=temp_lst.pop(0)
                #获取当前要处理的对象 根据路由
                now_dis_obj=page_use_compontent#赋值根对象
                for idx in temp_path_lst:#遍历索引 获取要处理对象
                    now_dis_obj=now_dis_obj['child']#获取vue组件 所有的子节点对象
                    now_dis_obj=now_dis_obj[idx]#获取要处理的子节点对象
                #添加使用的模板名称到 res中
                res.add(now_dis_obj["use_compontent"])
                #添加子组件对象的索引 / 路由 存入 待处理列表的最前方
                for new_idx in now_dis_obj['child']:
                    temp_lst.insert(0,copy.copy(temp_path_lst)+[new_idx])
        # print(res)
        return res
    #获取所有的模板信息
    def _get_template_info(self):
        file_lst=self._get_folder_paths3(pro_dir+os.sep+"_web"+os.sep+"web_vue_template")
        res=sorted(file_lst,key=lambda item:int(os.path.basename(item[1]).split('_')[0]) )
        return res
    #根据用到的模板 生成一个完整的 vue模板.js 文件
    def _create_vue_template_edit(self):
        str_js=''
        str_css=''
        file_lst=self._get_folder_paths2(pro_dir+os.sep+"_web"+os.sep+"web_vue_template")
        lstdir=sorted(file_lst,key=lambda item:int(os.path.basename(item).split('_')[0]) )
        for new_dir_path in lstdir:
            try:
                with open(new_dir_path+os.sep+'main.js','r',encoding='utf-8')as f:
                    str_js=str_js+'\n'+f.read()
            except Exception as e:pass
            try:
                with open(new_dir_path+ os.sep+'main.css','r',encoding='utf-8')as f:
                    str_css=str_css+'\n'+f.read()
            except Exception as e:pass
        with open(self.static_base_path+os.sep+'base_vue_template.js','w',encoding='utf-8')as f:
            f.write(str_js)

        with open(self.static_base_path+os.sep+'base_vue_template.css','w',encoding='utf-8')as f:
            f.write(str_css)
    
    #根据用到的模板 生成一个完整的 vue模板.js 文件
    def _create_vue_template(self):
        static_path=self.config['static_path']
        str_js=''
        str_css=''
        file_lst=self._get_folder_paths2(pro_dir+os.sep+"_web"+os.sep+"web_vue_template")
        lstdir=sorted(file_lst,key=lambda item:int(os.path.basename(item).split('_')[0]) )
        for new_dir_path in lstdir:
            try:
                with open(new_dir_path+os.sep+'main.js','r',encoding='utf-8')as f:
                    str_js=str_js+'\n'+f.read()
            except Exception as e:pass
            try:
                with open(new_dir_path+os.sep+'main.css','r',encoding='utf-8')as f:
                    str_css=str_css+'\n'+f.read()
            except Exception as e:pass
        print(static_path+os.sep+'base_vue_template2.js')
        with open(static_path+os.sep+'base_vue_template2.js','w',encoding='utf-8')as f:
            f.write(str_js)

        with open(static_path+os.sep+'base_vue_template2.css','w',encoding='utf-8')as f:
            f.write(str_css)
    def _get_folder_paths2(self,directory):  
        folder_paths = []  
        for root, dirs, files in os.walk(directory):  
            if 'main.js'in files:
                folder_paths.append(root)  # 将当前目录的完整路径添加到列表中  
        return folder_paths  
    #根据用到的 模板 生成python的后端代码并执行
    def _create_python_template(self):
        
        import_dir=self.config['project_dir_path']+os.sep+"_temp_dir"+os.sep+"python"
        try:shutil.rmtree(import_dir)  
        except:pass
        try:os.makedirs(import_dir)
        except:pass
        url_lst=[]#路由对应处理类
        #遍历得到所有使用的 模板名称
        for key,temp_dir_path in self.all_template_info:
            if key in self.all_use_template_names_set:
                # temp_dir_path#根据模板名称获取 该模板所在的文件夹
                #创建模块对应文件夹
                import_template_dir=import_dir+os.sep+str(key)
                #获取模板关联的 python文件 然后写入temp_dir文件夹中  并且导包
                python_file_path=temp_dir_path+os.sep+"main.py"
                if os.path.isfile(python_file_path):
                    try:os.makedirs(import_template_dir)
                    except:pass
                    with open(python_file_path,"rb")as f:
                        with open(import_template_dir+os.sep+"main.py","wb")as f2:
                            f2.write(f.read())
                    import_name=key+"_____import"
                    import_str="from _temp_dir.python.%s import main as %s"%(key,import_name)
                    exec(import_str)
                    if self.print_flag is True:print(import_str)
                    dir_res=dir(eval(import_name))
                    exec(import_name+'.g=self.g')
                    exec(import_name+'.config=self.config')
                    for item in dir_res:
                        #运行初始化函数
                        if "init_func_"==item[:len("init_func_")]:
                            try:
                                try_run_func=eval(import_name+"."+item)
                                try_run_func(self.config)
                            except:pass
                        #获取url_lst
                        elif "init_url_"==item[:len("init_url_")]:
                            url_lst.extend(eval(import_name+"."+item))
        print("#$######",url_lst)
        return url_lst
    def create_directory(self,path):  
        # 检查路径是否存在  
        try:
            if os.path.exists(path):  
                # 如果存在并且不为空，删除原有文件夹及其内容  
                shutil.rmtree(path)
        except:pass  
        # 创建新的空文件夹  
        os.mkdir(path)  
    # 打印完整节点树（转换为 HTML）  
    def prettify(self,elem):  
        """Return a pretty-printed XML string for the Element."""  
        rough_string = ET.tostring(elem, 'utf-8') .decode('utf-8')
        return rough_string
    #获取所有骨架类的信息，
    def get_all_gu_jia_info(self):
        self.gu_jia_set=set()
        skeleton_dir_path=pro_dir+os.sep+"_web"+os.sep+"web_vue_template"+os.sep+'页面编辑_被拖拽的组件1'+os.sep+'0_页面骨架'
        for item in os.listdir(skeleton_dir_path):  
            if os.path.isdir(os.path.join(skeleton_dir_path, item)):  
                with open(skeleton_dir_path+os.sep+item+os.sep+'main.yaml', 'r',encoding='utf-8') as f:
                    try:
                        temp_data = yaml.load(f, Loader=yaml.FullLoader)
                        self.gu_jia_set.add(temp_data['name'])
                    except:print('error:',skeleton_dir_path+os.sep+item+os.sep+'main.yaml','读取配置错误')
    def create_web_html(self):
        config=self.config
        dir_path=self.config['web_html_dir_path']
        self.get_all_gu_jia_info()#初始化骨架信息的获取
        #创建一个新的空文件夹
        self.create_directory(dir_path)
        #获取页面对象
        page_config=config['page_config']
        #遍历页面
        for key in page_config:
            #生成保存的路径名
            new_dir_path=dir_path+os.sep+page_config[key]['page_base_info']['page_name']
            #创建html文件夹
            os.mkdir(new_dir_path)
            #创建初始对象body
            body=ET.Element("body")
            #组件参数dct
            all_deepcopy_data={}
            # 在<sss>节点上插入新的<aaa>节点，并为其设置属性  
            gujia_base = ET.SubElement(body, "gu_jia1")
            gujia_base.set("id",page_config[key]['page_base_info']['page_name'])  
            #保存 idx 对应 节点对象 None为初始节点
            all_obj_dct={'None':gujia_base}
            node_data=page_config[key]['page_use_compontent']['page']
            new_node_lst=[['None',node_data['child']]]# [ [父节点索引,子节点数据]  ]
            while new_node_lst:
                new_node_lst2=[]
                for father_idx,child_dct in new_node_lst:
                    for key2 in child_dct:
                        idx=child_dct[key2]['idx']#当前索引
                        use_compontent=child_dct[key2]['use_compontent']#使用的vue组件名称
                        child=child_dct[key2]['child']#使用的vue组件名称
                        deepcopy_data=child_dct[key2]['data']#组件固定参数
                        all_obj_dct[idx]=ET.SubElement(all_obj_dct[father_idx],use_compontent) 
                        if use_compontent not in self.gu_jia_set:
                            ET.SubElement(all_obj_dct[idx],'tempxxxx') 
                        all_obj_dct[idx].set(":deepcopy_data", "data.data_"+str(idx))  
                        all_deepcopy_data["data_"+str(idx)]=deepcopy_data
                        new_node_lst2.append([idx,child])
                new_node_lst=new_node_lst2
            with open(new_dir_path+os.sep+'main.html','w',encoding='utf-8')as f:
                f.write(self.prettify(body))
            with open(new_dir_path+os.sep+'main.js','w',encoding='utf-8')as f:
                f.write(r'''new Vue({"el":"#%s","data":{"data":%s}})'''%(page_config[key]['page_base_info']['page_name'],f'{json.dumps(all_deepcopy_data)}'))
            
            # 将dict数据转换为YAML字符串  
            yaml_data = yaml.dump(page_config[key]['page_base_info'], allow_unicode=True, sort_keys=False, indent=4)  
            
            # 将YAML字符串写入文件  
            with open(new_dir_path+os.sep+'page_base_info.yaml', 'w',encoding='utf-8') as yaml_file:  
                yaml_file.write(yaml_data)

    #根据配置文件 生成web_html 以及生成路由对应函数列表
    def tornado_make_html_and_url(self):
        # config=self.config
        web_html_dir_path=self.config['web_html_dir_path']
        url_lst=[]#存放url对应处理类 [[url,class],...]
        #根据config  在web/we_html下生成 对应的html文件夹
        self.create_web_html()
        # page_name: 主页
        # url_flag: true
        # url: /home
        # page_idx: 9

        #根据已经生成的html文件夹内容 设置url_lst
        for dir_path in os.listdir(web_html_dir_path):
            new_dir_pathx=web_html_dir_path+os.sep+dir_path
            #读取基础信息
            with open(new_dir_pathx+os.sep+'page_base_info.yaml', 'r',encoding='utf-8') as f:
                base_info = yaml.load(f, Loader=yaml.FullLoader)
            if base_info.get("url_flag")==True:#如果开启路由功能 那么生成
                class_name=base_info.get('page_name')#类名为页面名称
                before_js=r'window.config_now = new data_update({type:"request",method:"get",url:"get_config_data",data:{},});config_now.get();'
                before_js2=r'config_now.add_before_func(function(data){data._temp_page_names={};for (let kk in data.page_config){data._temp_page_names[data.page_config[kk].page_base_info.page_name]=kk}data._temp_db_names={};for (let kk in data.db_config){data._temp_db_names[data.db_config[kk].table_base_info.名称]=kk}});'
                class_str=f"""
class {class_name}(base_class):
    def get(self):
        #main_html 读取到main.html内容
        with open(self.{class_name}_new_dir_pathx+os.sep+"main.html",'r',encoding='utf-8')as f: main_html=f.read()

        #main_js 读取到main.js内容
        with open(self.{class_name}_new_dir_pathx+os.sep+"main.js",'r',encoding='utf-8')as f: main_js=f.read()
        self.write(html_head_data+main_html+"<script>"+'{before_js}'+'{before_js2}'+main_js+"</script>")
    def post(self):
        #main_html 读取到main.html内容
        with open(self.{class_name}_new_dir_pathx+os.sep+"main.html",'r',encoding='utf-8')as f: main_html=f.read()[6:-7]
        #main_js 读取到main.js内容
        with open(self.{class_name}_new_dir_pathx+os.sep+"main.js",'r',encoding='utf-8')as f: main_js=f.read()
        self.write({{"main.html":main_html,"main.js":main_js}})
    """         
                # print(class_str)
                exec(class_str)#创建类
                exec(f"{class_name}.{class_name}_new_dir_pathx=new_dir_pathx")
                url_lst.append([base_info.get("url"),eval(f"{class_name}")])
        return url_lst
if __name__=="__main__":
    
    import json,os
        # 写入JSON文件  
    with open(os.path.dirname(os.path.abspath(__file__))+os.sep+'template_create.json', 'r', encoding='utf-8') as f:  
        data_dict = json.load(f)
    # def return_test_config():
    #     p=pro_dir+os.sep+"_test"+os.sep+"中文.yaml"
    #     with open(p, 'r',encoding='utf-8') as f:
    #         res = yaml.load(f, Loader=yaml.FullLoader)
    #     return res
    # config=return_test_config()
    template_create().run(data=data_dict)
    print("end")

